/**
 *  @file HttpParams.cpp
 */
#if defined(WIN32)
#   include <windows.h>
#   include <Winhttp.h>
#   pragma comment(lib, "Winhttp.lib")
#endif
#include "HttpParams.h"
#include "../../common/CppMemPool.h"
#include "../../common/CppflibMain.h"
#include "../SocketFactory.h"
#include "../../common/UtilFuncT.h"
#include "../../text/CFString.h"
#include "HttpException.h"
#include "../../util/FuncPtrT.h"
#include "../../collections/KeyValueListT.h"

using namespace cppflib::collections;
using namespace cppflib::lang;
using namespace cppflib::util;

namespace cppflib
{

namespace net
{

namespace http
{

PRIVATE struct _paramtype {
   HttpParams::param_t p;
   FuncPtr1T<bool, BaseObject*> chkFunc;
} paramTypeTbl[] = {
                      {HttpParams::P_CONNTIMEOUT,        FuncPtr1T<bool, BaseObject*>(&cppflib::InstanceOf<Int32>)}, 
                      {HttpParams::P_SOTIMEOUT,          FuncPtr1T<bool, BaseObject*>(&cppflib::InstanceOf<Int32>)}, 
                      {HttpParams::P_SOCKETFACTORY,      FuncPtr1T<bool, BaseObject*>(&cppflib::InstanceOf<SocketFactory>)}, 
                      {HttpParams::P_USERAGENT,          FuncPtr1T<bool, BaseObject*>(&cppflib::InstanceOf<CFString>)},
                      {HttpParams::P_SERVER,             FuncPtr1T<bool, BaseObject*>(&cppflib::InstanceOf<CFString>)},
                      {HttpParams::P_ISSERVER,           FuncPtr1T<bool, BaseObject*>(&cppflib::InstanceOf<Boolean>)},
                      {HttpParams::P_PROXY,              FuncPtr1T<bool, BaseObject*>(&cppflib::InstanceOf<NameValuePair>)},

                      {HttpParams::P_ENDPOINTSOCKET,     FuncPtr1T<bool, BaseObject*>(&cppflib::InstanceOf<Socket>)},
                      {HttpParams::P_NONE,               FuncPtr1T<bool, BaseObject*>(&cppflib::InstanceOf<BaseObject>)}
                   };

/**
 *  @return true if expected type matches that of the predefined type
 */
PRIVATE bool __IsTypeMatch(HttpParams::param_t p, BaseObject * pObj)
{
   for (int i = 0; paramTypeTbl[i].p != HttpParams::P_NONE; ++i) {
      if (paramTypeTbl[i].p == p) {
         if (paramTypeTbl[i].chkFunc(pObj))
            return true;

         break;
      }
   }

   throw HttpException(_S("Value's data type isn't the expected data type."));

   return false;
}

/**
 *  @return true if expected type matches that of the predefined type
 */
PRIVATE bool __IsTypeMatch(const BaseObject * pObj, const FuncPtr1T<bool, const BaseObject*> &chkFunc)
{
   if (chkFunc(pObj))
      return true;

   throw HttpException(_S("Value's data type isn't the data type to be retrieved."));

   return false;
}

/**
 *  Get system proxy settings. Currently, it works on Windows only
 */
PRIVATE NameValuePair * __GetSystemProxySettings()
{
   NameValuePair * pProxySettings = NULL;

#if defined(WIN32)
   WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = {0};
   
   do {
      if (!WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig))
         break;
   
      if (!proxyConfig.lpszProxy)
         break;

      CFString proxyAddr(proxyConfig.lpszProxy);
      CFString proxyPort;
      int pos = proxyAddr.Find(_S(":"));
      if (pos > 0) {
         proxyPort = proxyAddr.Mid(pos + 1);
         proxyAddr.Delete(pos, proxyAddr.GetLength() - pos);
      }
      else {
         proxyPort.Format(_S("%d"), http::HTTP_PORT); // use default port
      }
      
      pProxySettings = new NameValuePair(new CFString(proxyAddr), new CFString(proxyPort));
   } while(false);

   // free these manually
   if (proxyConfig.lpszAutoConfigUrl) GlobalFree(proxyConfig.lpszAutoConfigUrl);
   if (proxyConfig.lpszProxy) GlobalFree(proxyConfig.lpszProxy);
   if (proxyConfig.lpszProxyBypass) GlobalFree(proxyConfig.lpszProxyBypass);
   
#endif

   return pProxySettings;
}


HttpParams::HttpParams(void)
{
   this->pParamTbl = new HashtableT<Int32, BaseObject>();
   cppmempool::Retain(this->pParamTbl);

   // set default parameters
   SetParam(HttpParams::P_CONNTIMEOUT, 30 * 1000);
   SetParam(HttpParams::P_SOTIMEOUT, 30 * 1000);

   // proxy if any
   NameValuePair * pProxySettings = __GetSystemProxySettings();
   if (pProxySettings)
      SetParam(HttpParams::P_PROXY, pProxySettings);

   CFString tempStr;
   tempStr.Format(_S("Mozilla/5.0 (compatible; cppflib/%s)"), cppflib::GetflibVerString());
   SetParam(HttpParams::P_USERAGENT, static_cast<pcwstr_t>(tempStr));

   tempStr.Format(_S("HttpServer (cppflib/%s)"), cppflib::GetflibVerString());
   SetParam(HttpParams::P_SERVER, static_cast<pcwstr_t>(tempStr));
}

HttpParams::~HttpParams(void)
{
   if (pParamTbl)
      cppmempool::Release(pParamTbl);
}

///////////////////////////////////////////////////////////////////////////////////

bool HttpParams::Exists(param_t p) const
{
   Int32 key(static_cast<int>(p));
   return pParamTbl->ContainsKey(key);
}

const BaseObject * HttpParams::GetParam(param_t p) const
{
   Int32 key(static_cast<int>(p));
   BaseObject *pVal = pParamTbl->Get(key);
   if (!pVal)
      throw HttpException(_S("Parameter not found"));

   return pVal;
}

int HttpParams::GetIntParam(param_t p) const
{
   const BaseObject * pVal = GetParam(p);   
   __IsTypeMatch(pVal, FuncPtr1T<bool, const BaseObject*>(&cppflib::InstanceOf<Int32>));
   return static_cast<const Int32*>(pVal)->Value();
}

bool HttpParams::GetBooleanParam(param_t p) const
{
   const BaseObject * pVal = GetParam(p);
   __IsTypeMatch(pVal, FuncPtr1T<bool, const BaseObject*>(&cppflib::InstanceOf<Boolean>));
   return static_cast<const Boolean*>(pVal)->Value();
}

const CFString * HttpParams::GetStrParam(param_t p) const
{
   const BaseObject * pVal = GetParam(p);
   __IsTypeMatch(pVal, FuncPtr1T<bool, const BaseObject*>(&cppflib::InstanceOf<CFString>));
   return static_cast<const CFString*>(pVal);
}

//////////////////////////////////////////////////////////////////////////////////

void HttpParams::SetParam(param_t p, BaseObject * pVal)
{
   if (!pVal)
      throw HttpException(_S("Value cannot be NULL"));

   __IsTypeMatch(p, pVal);
   pParamTbl->Set(new Int32(static_cast<int>(p)), pVal);
}

void HttpParams::SetParam(param_t p, int val)
{
   SetParam(p, new Int32(val));
}

void HttpParams::SetParam(param_t p, bool val)
{
   SetParam(p, new Boolean(val));
}

void HttpParams::SetParam(param_t p, pcwstr_t pStr)
{
   SetParam(p, new CFString(pStr));
}

void HttpParams::RemoveParam(param_t p)
{
   pParamTbl->Remove(Int32(static_cast<int>(p)));
}

} // end of namespace http

} // end of namespace net

} // end of namespace cppflib


